/* Includes ------------------------------------------------------------------*/
#include <main.h>

/* Private define-------------------------------------------------------------*/
#define CCP_S1  BIT5
#define CCP_S0  BIT4   

#define EPC0H   BIT1
#define EPC0L   BIT0
/* Private variables----------------------------------------------------------*/
static void PWM_Init();
static void PWM_LED_Adjust_Brightness();

static void PWM_Duty_Set(uint8_t );

static void IAP_Backup(uint16_t ,uint8_t );
static uint8_t IAP_Restore(uint16_t );
/* Public variables-----------------------------------------------------------*/
PWM_t PWM = 
{
  Duty_20,
  PWM_Init,
  PWM_LED_Adjust_Brightness,
  PWM_Duty_Set,
  /*初始化的函数名跟函数指针名不一样，这是因为这两个函数当作通用函数，其他文件也能使用，
  函数指针的名字仅拿来当PWM备份占空比使用*/
  IAP_Backup,               
  IAP_Restore
  };
/* Private function prototypes------------------------------------------------*/

/*
* @name   PWM_Init
* @brief  PWM初始化
* @param  None
* @retval None   
*/
static void PWM_Init()
{
    //选择管脚，因为开发板的PWM灯接到了P3.5口
    //将BIT5的CCP_S1清0，BIT4的CCP_S0置1，即可将CCP切换到P3.5管脚
    AUXR1 &= ~(CCP_S1); 
    AUXR1 |= (CCP_S0);

    //CCON里都是一些标志位，全置0即可
    /*CMOD 的BIT7置0，设置空闲模式下PAC计数器继续工作;BIT3、BIT2、BIT1置为110，
    系统时钟6分频，SYSclk/6*/
    CCON = 0x00;
    CMOD = 0x0C;

    //用于保存PCA装载值的16位计数器都清零
    CL = 0;
    CH = 0;

    //BIT6置1，允许比较器功能；BIT1置1，允许CCP0脚用作脉宽调节输出
    //PCA_PWM0的BIT7和BIT6置0，使模块工作于8位PWM模式，BIT1和BIT0的EPC0H和EPC0L清0
    CCAPM0 = 0x42;
    PCA_PWM0 = 0x00;

    //恢复占空比
    PWM.Duty = PWM.IAP_Duty_Restore(IAP_PWM_DUTY_ADDR);

    //设置占空比
    PWM_Duty_Set(PWM.Duty);
		
    CR = 1;
}

/*
* @name   PWM_LED_Adjust_Brightness
* @brief  PWM灯调整亮度
* @param  None
* @retval None   
*/
static void PWM_LED_Adjust_Brightness()
{
    if(KEY2.KEY_Flag == TRUE)
    {
        //单击 亮度 0-20-40-60-80-100-0 循环调节
        //双击 亮度 100
        //长按 亮度 0
        if(KEY2.Click == TRUE)
        {
            switch (PWM.Duty)
            {
              case Duty_0:    PWM.Duty = Duty_20; break;
              case Duty_20:   PWM.Duty = Duty_40; break;
              case Duty_40:   PWM.Duty = Duty_60; break;
              case Duty_60:   PWM.Duty = Duty_80; break;
              case Duty_80:   PWM.Duty = Duty_100;break;
              case Duty_100:  PWM.Duty = Duty_0;  break;
              default: PWM.Duty = Duty_0; break;
            }

            #ifdef Monitor_Run_Code
              printf("KEY2 click detected\r\n\r\n");    //打印单击信息
            #endif
        }
        
        //设置占空比，调整亮度
        PWM_Duty_Set(PWM.Duty);
        
        //标志位清零
        KEY2.KEY_Flag     = FALSE;
        KEY2.Click        = FALSE;

        //喂狗
        WatchDog.Feed();
        
        //备份占空比
        PWM.IAP_Duty_Backup(IAP_PWM_DUTY_ADDR,PWM.Duty);
    }
}

/*
* @name   PWM_Duty_Set
* @brief  PWM灯调整亮度
* @param  None
* @retval None   
*/
static void PWM_Duty_Set(uint8_t PWM_Duty)
{
  uint8_t  Temp_Value = 0;
  /*初始化时PWM.Duty的值为Duty_20，单击按下后，进入该switch语句
  PWM.Duty 被修改为Duty_40，占空比变量Temp_Value被赋值153，然后
  跳出switch语句，后面对CCAP0H赋值，输出占空比
  下次再单击按键，进入switch，匹配case Duty_40，所以PWM.Duty会
  再次被改变为Duty_60，占空比输出102即60%*/
  switch (PWM_Duty)
  {
    case Duty_0:    break;
    case Duty_20:   Temp_Value = 204; break;
    case Duty_40:   Temp_Value = 153; break;
    case Duty_60:   Temp_Value = 102; break;
    case Duty_80:   Temp_Value = 51;  break;
    case Duty_100:  break;
    default: PWM_Duty = Duty_0; break;
  }
  //亮度调节
  //占空比为0%，全输出低电平，PWM灯灭
  if(PWM.Duty == 0)
  {
    PCA_PWM0 |= (EPC0H);
    CCAP0H = 0xFF;

    PCA_PWM0 |= (EPC0L);		//置1则表示9位数，加上CCAP0L最大去到511
    CCAP0L = CCAP0H;
  }
  //占空比为100%，全输出高电平，PWM灯全亮
  else if(PWM.Duty == 100)
  {
    PCA_PWM0 &= ~(EPC0H);
    CCAP0H = 0x00;
    PCA_PWM0 &= ~(EPC0L);
    CCAP0L = 0x00;
  }
  else
  {
    //根据Temp_Value设置的值修改占空比
    PCA_PWM0 &= ~(EPC0H);	//~0000 0010  ->  1111 1101	即让EPC0H为0
    CCAP0H = Temp_Value;
    PCA_PWM0 &= ~(EPC0L);	//因为上一步将BIT0，即EPC0L位置1了，所以也让该位清0后，CCAP0H再赋值给CCAP0L
    CCAP0L = CCAP0H;
  }
}

/*
* @name   IAP_Backup
* @brief  通过IAP备份
* @param  IAP_Addr：备份地址
* @param  IAP_Data：备份数据
* @retval None   
*/
static void IAP_Backup(uint16_t IAP_Addr,uint8_t IAP_Data)
{
  IAP.ucIAP_Cnt = IAP_CNT;  //写入的次数，多次写入都失败的话，单片机会死机的，所以要保证次数
	
  while(IAP.ucIAP_Cnt--)
  {
    IAP.IapEraseSector(IAP_Addr);   //写数据前要把扇区擦除
    IAP.IapProgramByte(IAP_Addr,IAP_Data);    //写入数据
    IAP.ucIAP_Flag = TRUE;          //假如写成功了
    
    if(IAP.IapReadByte(IAP_Addr) != IAP_DATA)
    {
      IAP.ucIAP_Flag = FALSE;       //不成功
    }
    //判断是否写成功
    if(IAP.ucIAP_Flag == TRUE)    //若写成功，则退出循环
    {
      break;
    }
  }
}

/*
* @name   IAP_Restore
* @brief  通过IAP恢复
* @param  IAP_Addr：恢复地址
* @retval 恢复值   
*/
static uint8_t IAP_Restore(uint16_t IAP_Addr)
{
  //考虑产品稳定性，定义了两个变量，读两次
  uint16_t Read_IAP_Para1,Read_IAP_Para2;
  uint8_t IAP_Data = 0;
  
  //从EEPROM读取数据
  Read_IAP_Para1 = IAP.IapReadByte(IAP_Addr);
  Read_IAP_Para2 = IAP.IapReadByte(IAP_Addr);

  //判断两次读取是否一致
  if(Read_IAP_Para1 == Read_IAP_Para2)
  {
    if(Read_IAP_Para1 != 0xFF)  //如果读出来不是FF说明之前已经有备份
    {
      IAP_Data = Read_IAP_Para1;
    }
    else
    {
      IAP_DATA = Duty_0;        //从来就没备份写入过数据，就让灯灭
    }
  }
  //两次读取不一致
  else
  {
    IAP_Data = Duty_0;          //让PWM灯灭，也可以做其他处理，比如重启
  }
  return IAP_Data;
}

/********************************************************
  End Of File
********************************************************/